package course;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import people.professor.Professor;
import people.student.Student;
import permission.RoleManager;
import permission.Roleable;
import permission.Secureable;
import permission.role.CourseLeaderRole;
import permission.role.Role;
import course.requirements.Requirement;
import course.requirements.RequirementManager;

/**
 * 
 */

/**
 * @author Andreas Waldherr (e1028787@student.tuwien.ac.at)
 *
 */
public abstract class AbstractCourse implements Secureable {
	private String courseID;
	private String courseName;
	private Date registerStart;
	private Date registerEnd;
	private Date unregisterEnd;
	private HashSet<Student> registeredStudents;
	private Professor courseLeader;
	private List<Requirement<?>> requirements;
	
	private final List<Role> ACCESS_ROLES = new ArrayList<Role>();

	/**
	 * 
	 * @param courseID
	 * @param courseName
	 * @param registerStart
	 * @param registerEnd
	 * @param unregisterEnd
	 */
	protected AbstractCourse(String courseID, String courseName, Professor courseLeader, Date registerStart, Date registerEnd, Date unregisterEnd){
		setCourseID(courseID);
		setCourseName(courseName);
		setRegisterStart(registerStart);
		setRegisterEnd(registerEnd);
		setUnregisterEnd(unregisterEnd);
		setRegisteredStudents(new HashSet<Student>());
		setCourseLeader(courseLeader);
	}

	/**
	 * @return the courseID
	 */
	public String getCourseID() {
		return courseID;
	}

	/**
	 * @param courseID the courseID to set
	 */
	protected void setCourseID(String courseID) {
		this.courseID = courseID;
	}

	/**
	 * @return the courseName
	 */
	public String getCourseName() {
		return courseName;
	}

	/**
	 * @param courseName the courseName to set
	 */
	public void setCourseName(String courseName) {
		this.courseName = courseName;
	}

	/**
	 * @return the registerStart
	 */
	public Date getRegisterStart() {
		return registerStart;
	}

	/**
	 * @param registerStart the registerStart to set
	 */
	public void setRegisterStart(Date registerStart) {
		this.registerStart = registerStart;
	}

	/**
	 * @return the registerEnd
	 */
	public Date getRegisterEnd() {
		return registerEnd;
	}

	/**
	 * @param registerEnd the registerEnd to set
	 */
	public void setRegisterEnd(Date registerEnd) {
		this.registerEnd = registerEnd;
	}

	/**
	 * @return the unregisterEnd
	 */
	public Date getUnregisterEnd() {
		return unregisterEnd;
	}

	/**
	 * @param unregisterEnd the unregisterEnd to set
	 */
	public void setUnregisterEnd(Date unregisterEnd) {
		this.unregisterEnd = unregisterEnd;
	}

	/**
	 * @return the registeredStudents
	 */
	public Collection<Student> getRegisteredStudents(Roleable person) {
		boolean granted = RoleManager.INSTANCE.isAccessGranted(this, person);
		if(granted) {
			return registeredStudents;
		}
		return new ArrayList<Student>();
	}

	/**
	 * @param registeredStudents the registeredStudents to set
	 */
	protected void setRegisteredStudents(HashSet<Student> registeredStudents) {
		this.registeredStudents = registeredStudents;
	}
	
	/**
	 * @return Number of registered Students.
	 */
	public int getNumberOfRegistrations(){
		return getRegisteredStudents(getCourseLeader()).size();
	}
	
	public Professor getCourseLeader() {
		return courseLeader;
	}

	public void setCourseLeader(Professor courseLeader) {
		this.courseLeader = courseLeader;
	}
	
	public void addRequirement(Roleable role, Requirement<?> req) {
		if(role == null || req == null) {
			throw new IllegalArgumentException("both parameters can not be null");
		}
		
		List<CourseLeaderRole> courseLeaderRoles = RoleManager.INSTANCE.getCourseLeaderRoles(this, role);
		boolean isAdministrator = RoleManager.INSTANCE.isAdministrator(role);
		if(isAdministrator || !courseLeaderRoles.isEmpty()) {
			if(requirements == null) {
				requirements = new ArrayList<Requirement<?>>();
			}
			requirements.add(req);
		}
	}
	
	public Collection<Course> getRequirements() {
		return RequirementManager.INSTANCE.getCourseListFromRequirementList(requirements);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((courseID == null) ? 0 : courseID.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		AbstractCourse other = (AbstractCourse) obj;
		if (courseID == null) {
			if (other.courseID != null) {
				return false;
			}
		} else if (!courseID.equals(other.courseID)) {
			return false;
		}
		return true;
	}
	
	public String toString(){
		String ret = "";
		
		ret += "LVA " + getCourseID() + ": " + getCourseName() + "\n" +
						"\tRegistration: " + getRegisterStart() + " - " + getRegisterEnd() + "\n" +
						"\tUn-register till: " + getUnregisterEnd() + "\n" + 
						getNumberOfRegistrations() + " already registered.";
		
		return ret;
	}
	
	@Override
	public Collection<Role> getAccessRoles() {
		return ACCESS_ROLES;
	}
	
	@Override
	public void addAccessRole(Role role) {
		ACCESS_ROLES.add(role);
	}
}
